# -*- coding: utf-8 -*-
import random
from typing import Sequence

from fastapi import Request

from sqlalchemy import Select
from sqlalchemy.ext.asyncio import AsyncSession

from App.app.admin.crud.sys_user_crud import SysUserDao
from App.app.admin.models.sys_user import SysUser
from App.app.admin.schemas.sys_user_schema import CreateSysUserParam, UpdateSysUserParam, RegisterUserParam, \
    ResetPasswordParam, SysUserSchema, UpdateMeParam
from App.common.exception import errors
from App.common.exception.errors import CustomError, ForbiddenError, NotFoundError
from App.common.security.jwt import superuser_verify, get_token, get_hash_password, password_verify
from App.config.conf import settings
from App.database.mysql import async_db_session
from App.database.redis import redis_client
from App.utils.re_verify import is_phone


class SysUserService:
    @staticmethod
    async def get(*, pk: int) -> SysUser:
        async with async_db_session() as db:
            sys_user = await SysUserDao.get_with_role(db, user_id=pk)
            if not sys_user:
                raise NotFoundError(message='用户不存在')
            return sys_user

    @staticmethod
    async def get_select(*, nickname: str = None, username: str = None, phone: str = None, status: str = None,
                         role_id: str = None, sort_field: str = None, sort_order: str = None) -> Select:
        return await SysUserDao.get_list(nickname=nickname, username=username, phone=phone, status=status,
                                         role_id=role_id, sort_field=sort_field, sort_order=sort_order)

    @staticmethod
    async def get_all() -> Sequence[SysUser]:
        async with async_db_session() as db:
            sys_users = await SysUserDao.get_all(db)
            return sys_users

    @staticmethod
    async def register_user(obj_in: RegisterUserParam) -> SysUser:
        async with async_db_session.begin() as db:

            if not obj_in.password:
                raise CustomError(message='密码为空')
            username = await SysUserDao.get_by_username(db, obj_in.username)

            if username:
                raise errors.CustomError(message='用户已注册')

            obj_in.nickname = obj_in.nickname if obj_in.nickname else f'匿名用户#{random.randrange(10000, 88888)}'

            email = await SysUserDao.check_email(db, obj_in.email)

            if email:
                raise errors.CustomError(message='邮箱已注册')
            return await SysUserDao.register_user(db, obj_in)

    @staticmethod
    async def create(*, request: Request, obj_in: CreateSysUserParam) -> SysUserSchema:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            username = await SysUserDao.get_by_username(db, obj_in.username)
            if username:
                raise CustomError(message='用户已注册')

            obj_in.nickname = obj_in.nickname if obj_in.nickname else f'匿名用户#{random.randrange(10000, 88888)}'

            if obj_in.email:
                email = await SysUserDao.check_email(db, obj_in.email)
                if email:
                    raise CustomError(message='邮箱已注册')
            if obj_in.phone:
                if not is_phone(obj_in.phone):
                    raise CustomError(message="手机格式不正确")
                phone = await SysUserDao.check_phone(db, obj_in.phone)
                if phone:
                    raise CustomError(message="手机已注册")
            data = await SysUserDao.create(db, obj_in)
            return SysUserSchema.model_validate(data)

    @staticmethod
    async def update(*, request: Request, obj_in: UpdateSysUserParam) -> int:
        async with async_db_session.begin() as db:
            if not request.user.is_super:
                if request.user.id != obj_in.id:
                    raise ForbiddenError(message='你只能修改自己的信息')
            await SysUserService._check_input(db, obj_in)
            count = await SysUserDao.update(db, obj_in.id, obj_in)
            await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{obj_in.id}')
            return count

    @staticmethod
    async def delete(*, pk: int) -> int:
        async with async_db_session.begin() as db:
            count = await SysUserDao.delete(db, pk)
            return count

    @staticmethod
    async def logoff(*, pk: int) -> int:
        async with async_db_session.begin() as db:
            sys_user = await SysUserDao.get(db, pk)
            if not sys_user:
                raise errors.NotFoundError(message='用户不存在')
            count = await SysUserDao.logoff(db, pk)
            return count

    @staticmethod
    async def update_super(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            if not await SysUserDao.get(db, pk):
                raise NotFoundError(message='用户不存在')
            else:
                if pk == request.user.id:
                    raise ForbiddenError(message='非法操作，自己无法修改自己的权限')
                super_status = await SysUserDao.get_super(db, pk)
                count = await SysUserDao.set_super(db, pk, 0 if super_status else 1)
                await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{pk}')
                return count

    @staticmethod
    async def update_staff(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            if not await SysUserDao.get(db, pk):
                raise NotFoundError(message='用户不存在')
            else:
                if pk == request.user.id:
                    raise ForbiddenError(message='非法操作，自己无法修改自己的权限')
                staff_status = await SysUserDao.get_staff(db, pk)
                count = await SysUserDao.set_staff(db, pk, 0 if staff_status else 1)
                await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{pk}')
                return count

    @staticmethod
    async def update_status(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            if not await SysUserDao.get(db, pk):
                raise NotFoundError(message='用户不存在')
            else:
                if pk == request.user.id:
                    raise ForbiddenError(message='非法操作，自己无法修改自己账号状态')
                status = await SysUserDao.get_status(db, pk)
                count = await SysUserDao.set_status(db, pk, 0 if status else 1)
                await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{pk}')
                return count

    @staticmethod
    async def update_multi_login(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            if not await SysUserDao.get(db, pk):
                raise NotFoundError(message='用户不存在')
            else:
                user_id = request.user.id
                multi_login = await SysUserDao.get_multi_login(db, pk) if pk != user_id else request.user.is_multi_login
                count = await SysUserDao.set_multi_login(db, pk, 0 if multi_login else 1)
                token = get_token(request)
                # 超级用户修改自身时，除当前token外，其他token失效
                if pk == user_id:
                    key_prefix = f'{settings.TOKEN_REDIS_PREFIX}:{pk}'
                    await redis_client.delete_prefix(key_prefix, exclude=f'{key_prefix}:{token}')
                # 超级用户修改他人时，其他token将全部失效
                else:
                    key_prefix = f'{settings.TOKEN_REDIS_PREFIX}:{pk}'
                    await redis_client.delete_prefix(key_prefix)
                    key_prefix = f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{pk}'
                    await redis_client.delete_prefix(key_prefix)
                    key = f'{settings.JWT_USER_REDIS_PREFIX}:{pk}'
                    await redis_client.delete(key)
                return count

    @staticmethod
    async def pwd_reset(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            superuser_verify(request)
            if not await SysUserDao.get(db, pk):
                raise NotFoundError(message='用户不存在')
            else:
                if pk == request.user.id:
                    raise ForbiddenError(message='非法操作，无法重置自己的登录密码。请使用修改密码功能更改登录密码！')
                new_pwd = get_hash_password("123456")
                count = await SysUserDao.reset_password(db, pk, new_pwd)
                # 重置密码后，需将该用户的所有TOKEN删除
                key_prefix = [
                    f'{settings.TOKEN_REDIS_PREFIX}:{pk}',
                    f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{pk}',
                    f'{settings.JWT_USER_REDIS_PREFIX}:{pk}',
                ]
                for prefix in key_prefix:
                    await redis_client.delete_prefix(prefix)
                return count

    @staticmethod
    async def pwd_change(*, request: Request, obj_in: ResetPasswordParam) -> int:
        async with async_db_session.begin() as db:
            user = await SysUserDao.get(db, request.user.id)
            if not password_verify(obj_in.old_password, str(user.password)):
                raise CustomError(message='原密码错误')
            new_password = obj_in.new_password
            repeat_password = obj_in.repeat_password
            if new_password != repeat_password:
                raise CustomError(message='密码输入不一致')
            if password_verify(obj_in.new_password, str(user.password)):
                raise CustomError(message='新密码不能和原密码一致')

            new_pwd = get_hash_password(obj_in.new_password)

            count = await SysUserDao.reset_password(db, request.user.id, new_pwd)

            # 修改密码后，将当前用户的所有TOKEN删除
            key_prefix = [
                f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}',
                f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{request.user.id}',
                f'{settings.JWT_USER_REDIS_PREFIX}:{request.user.id}',
            ]

            for prefix in key_prefix:
                await redis_client.delete_prefix(prefix)
            return count

    @staticmethod
    async def update_me(*, obj_in: UpdateMeParam | UpdateSysUserParam) -> int:
        async with async_db_session.begin() as db:
            await SysUserService._check_input(db, obj_in)
            count = await SysUserDao.update(db, obj_in.id, obj_in)
            await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{obj_in.id}')
            return count

    @staticmethod
    async def _check_input(db: AsyncSession, obj_in: UpdateMeParam | UpdateSysUserParam):
        input_user = await SysUserDao.get(db, obj_in.id)
        if not input_user:
            raise NotFoundError(message='用户不存在')
        if input_user.email != obj_in.email:
            if obj_in.email:
                email = await SysUserDao.check_email(db, obj_in.email)
                if email:
                    raise CustomError(message='邮箱已注册')
        if input_user.phone != obj_in.phone:
            if obj_in.phone:
                if not is_phone(obj_in.phone):
                    raise CustomError(message="手机格式不正确")
                phone = await SysUserDao.check_phone(db, obj_in.phone)
                if phone:
                    raise CustomError(message='手机号码已注册')
